﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class ObjectManager : Singleton<ObjectManager>
{
    /// <summary>
    /// 对象池节点
    /// </summary>
    protected Transform m_RecycleTs;
    /// <summary>
    /// 场景节点
    /// </summary>
    protected Transform m_SceneTs;

    /// <summary>
    /// 使用中的对象 对象池
    /// </summary>
    protected Dictionary<uint, List<ResourceObj>> m_ObjectPoolDic = new Dictionary<uint, List<ResourceObj>>();
    /// <summary>
    /// 暂存的Resourceobj Dic
    /// </summary>
    protected Dictionary<int, ResourceObj> m_RescourceObjDic = new Dictionary<int, ResourceObj>();
    /// <summary>
    /// ResourceObj 对象池
    /// </summary>
    protected ClassObjectPool<ResourceObj> m_ResourceObjPool = null;
    /// <summary>
    /// 根据异步的guid 存储ResourceObj 来判断是否时异步加载
    /// </summary>
    protected Dictionary<int, ResourceObj> m_AsyncResObjs = new Dictionary<int, ResourceObj>();

    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="recyclets"></param>
    public void Init(Transform recyclets,Transform scenets)
    {
        m_ResourceObjPool = ObjectManager.Instance.GetOrCreateClassPool<ResourceObj>( 500);
        this.m_RecycleTs = recyclets;
        this.m_RecycleTs.gameObject.SetActive(false);
        this.m_SceneTs = scenets;
    }

    /// <summary>
    /// 清空对象池
    /// </summary>
    public void ClearCache()
    {
        List<uint> tempList = new List<uint>();

        foreach (uint key in m_ObjectPoolDic.Keys)
        {
            List<ResourceObj> resobjlist = m_ObjectPoolDic[key];
            for (int i = resobjlist.Count-1; i >=0; i--)
            {
                ResourceObj obj=resobjlist[i];
                //可以克隆 且 可以被清除
                if (!System.Object.ReferenceEquals(obj.m_CloneObj,null)&&obj.m_bClear)
                {
                    resobjlist.Remove(obj);
                    m_RescourceObjDic.Remove(obj.m_CloneObj.GetInstanceID());
                    GameObject.Destroy(obj.m_CloneObj);
                    obj.Reset();
                    m_ResourceObjPool.Recycle(obj);
                }
            }
            ///判断资源是否全部被清除
            if (resobjlist.Count<=0)
            {
                tempList.Add(key);
            }
        }

        ///清除已经卸载 的资源对象池
        for (int i = 0; i < tempList.Count; i++)
        {
            uint tempcrc = tempList[i];
            if (m_ObjectPoolDic.ContainsKey(tempcrc))
            {
                m_ObjectPoolDic.Remove(tempcrc);
            }
        }
        tempList.Clear();
    }


    /// <summary>
    /// 清除某个资源再对象池中所有的资源
    /// </summary>
    public void ClearPoolObject(uint crc)
    {
        List<ResourceObj> reslist = null;
        if (!m_ObjectPoolDic.TryGetValue(crc, out reslist) || reslist == null) return;

        for (int i = reslist.Count-1; i >=0; i--)
        {
            ResourceObj resobj = reslist[i];
            //资源可以被清除
            if (resobj.m_bClear)
            {
                reslist.Remove(resobj);
                int tempId = resobj.m_CloneObj.GetInstanceID();
                GameObject.Destroy(resobj.m_CloneObj);
                resobj.Reset();
                m_RescourceObjDic.Remove(tempId);
                m_ResourceObjPool.Recycle(resobj);
            }
        }
        //对象池中的资源为0进行删除
        if (reslist.Count<=0)
        {
            m_ObjectPoolDic.Remove(crc);
        }
    }

    /// <summary>
    /// 根据对象实例直接获取离线数据
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public OffLineData FindOffLineData(GameObject obj)
    {
        OffLineData offLineData = null;
        ResourceObj resobj = null;
        m_RescourceObjDic.TryGetValue(obj.GetInstanceID(),out resobj);
        if (resobj!=null)
        {
            offLineData = resobj.m_offLineData;
        }
        return offLineData;
    }


    /// <summary>
    /// 从对象池里面取对象
    /// </summary>
    /// <param name="path"></param>
    /// <param name="bclear"></param>
    /// <returns></returns>
    protected ResourceObj GetResourceObjFromPool(uint crc)
    {
        List<ResourceObj> list = null;
        if (this.m_ObjectPoolDic.TryGetValue(crc,out list)&&list!=null&&list.Count>0)
        {
            ///resourceManager 的引用计数
            ResourceManager.Instance.IncreaseResourceRef(crc);
            ResourceObj resourceObj = list[0];
            list.RemoveAt(0);
            GameObject obj = resourceObj.m_CloneObj;
            if (!System.Object.ReferenceEquals(obj,null))
            {
                if (!System.Object.ReferenceEquals(resourceObj.m_offLineData,null))
                {
                    resourceObj.m_offLineData.ResetPrpo();
                }
                resourceObj.m_Already = false;
#if UNITY_EDITOR
                if (obj.name.EndsWith("(Recycle)"))
                {
                    obj.name = obj.name.Replace("(Recycle)","");
                }
#endif
            }
            return resourceObj;
        }
        return null;
    }

    /// <summary>
    /// 取下异步加载
    /// </summary>
    public void CancelAsyncLoad(int guid)
    {
        ResourceObj resobj = null;
        if (m_AsyncResObjs.TryGetValue(guid,out resobj)&&ResourceManager.Instance.CancelAsyncLoad(resobj))
        {
            m_AsyncResObjs.Remove(guid);
            resobj.Reset();
            m_ResourceObjPool.Recycle(resobj);
        }
    }


    /// <summary>
    /// 是否正在异步加载
    /// </summary>
    /// <returns></returns>
    public bool IsAsyncLoading(int guid)
    {
        return m_AsyncResObjs.ContainsKey(guid);
    }


    /// <summary>
    /// 该对象是否是对象池创建的
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public bool IsObjectManangerCreate(GameObject obj)
    {
        int guid = obj.GetInstanceID();
        return m_RescourceObjDic.ContainsKey(guid);
    }



    /// <summary>
    /// 预加载路径
    /// </summary>
    /// <param name="path">路径</param>
    /// <param name="count">预加载个数</param>
    /// <param name="clear">场景跳转是否清除</param>
    public void PreLoadGameobject(string path,int count=1,bool clear=false)
    {
        List<GameObject> tempGameobjectList = new List<GameObject>();
        for (int i = 0; i < count; i++)
        {
            GameObject obj = InstantiateObject(path,false,clear);
            tempGameobjectList.Add(obj);
        }
        for (int i = 0; i < count; i++)
        {
            GameObject obj = tempGameobjectList[i];
            ReleaseObject(obj);
            obj = null;
        }
        tempGameobjectList.Clear();
    }

    /// <summary>
    /// 同步加载
    /// </summary>
    /// <param name="path"></param>
    /// <param name="bclear"></param>
    /// <returns></returns>
    public GameObject InstantiateObject(string path, bool isSceneObj=false,bool bclear = true)
    {
        uint crc = Crc32.GetCrc32(path);
        ResourceObj resourceObj = GetResourceObjFromPool(crc);
        if (resourceObj==null)
        {
            resourceObj = m_ResourceObjPool.Spwan(true);
            resourceObj.m_Crc = crc;
            resourceObj.m_bClear = bclear;
            //resourcemanager提供加载方法
            resourceObj= ResourceManager.Instance.LoadRecource(path, resourceObj);
            if (resourceObj.m_ResItem.m_Obj != null)
            {
                resourceObj.m_CloneObj = GameObject.Instantiate(resourceObj.m_ResItem.m_Obj) as GameObject;
                ///赋值离线加载数据
                resourceObj.m_offLineData = resourceObj.m_CloneObj.GetComponent<OffLineData>();
            }
        }
        if (isSceneObj) resourceObj.m_CloneObj.transform.SetParent(this.m_SceneTs, false);

        int tempId = resourceObj.m_CloneObj.GetInstanceID();
        if (!m_RescourceObjDic.ContainsKey(tempId))
            m_RescourceObjDic.Add(tempId,resourceObj);

        return resourceObj.m_CloneObj;
    }

    /// <summary>
    /// 异步资源加载
    /// </summary>
    /// <param name="path"></param>
    /// <param name="finishCallback">加载完成回调</param>
    /// <param name="loadResPriority"></param>
    /// <param name="isSceneObj"></param>
    /// <param name="param1"></param>
    /// <param name="param2"></param>
    /// <param name="param3"></param>
    /// <param name="bclear">跳场景是否删除</param>
    /// <returns></returns>
    public long InstantiateObjectAsync(string path, OnAsyncObjFinishDele finishCallback, LoadResPriority loadResPriority, bool isSceneObj = false, object param1 = null, object param2 = null, object param3 = null, bool bclear=true)
    {
        if (string.IsNullOrEmpty(path)) return 0;
        uint crc = Crc32.GetCrc32(path);
        ResourceObj obj = GetResourceObjFromPool(crc);
        if (obj!=null)
        {
            if (isSceneObj)
                obj.m_CloneObj.transform.SetParent(this.m_SceneTs,false);
            finishCallback?.Invoke(path, obj.m_CloneObj, param1, param2, param3);
            return obj.m_Guid;
        }

        int guid = ResourceManager.Instance.CreateGuid();
        obj = m_ResourceObjPool.Spwan(true);
        obj.m_Crc = crc;
        obj.m_bClear = bclear;
        obj.m_setSceneParent = isSceneObj;
        obj.m_FinishCallback = finishCallback;
        obj.m_Guid = guid;
        obj.Param1 = param1;
        obj.Param2 =param2;
        obj.Param3 = param3;

        //调用resourmanager 异步加载
        ResourceManager.Instance.AsnycLoadInstanceResource(path,obj, OnLoadInstanceObjFinish, loadResPriority,param1,param2,param3,crc);

        return guid;
    }

    /// <summary>
    ///  当实例化资源加载完毕时的回调
    /// </summary>
    /// <param name="path">路径</param>
    /// <param name="obj">中间类</param>
    /// <param name="param1"></param>
    /// <param name="param2"></param>
    /// <param name="param3"></param>
    protected void OnLoadInstanceObjFinish(string path, ResourceObj resobj, object param1 = null, object param2 = null, object param3 = null)
    {
        if (resobj == null) return;
        if (resobj.m_ResItem.m_Obj==null)
        {
#if UNITY_EDITOR
            Debug.LogError("异步加载实例化资源错误:"+path);
#endif
        }
        else
        {
            resobj.m_CloneObj = GameObject.Instantiate(resobj.m_ResItem.m_Obj) as GameObject;
            ///赋值离线加载数据
            resobj.m_offLineData = resobj.m_CloneObj.GetComponent<OffLineData>();
        }
        if (resobj.m_CloneObj!=null&&resobj.m_setSceneParent)
        {
            resobj.m_CloneObj.transform.SetParent(this.m_SceneTs,false);
        }

        ///加载完成从正在加载的异步中移除
        if (m_AsyncResObjs.ContainsKey(resobj.m_Guid))
        {
            m_AsyncResObjs.Remove(resobj.m_Guid);
        }


        if (resobj.m_FinishCallback!=null)
        {
            int tempId = resobj.m_CloneObj.GetInstanceID();
            if (!m_RescourceObjDic.ContainsKey(tempId))
            {
                m_RescourceObjDic.Add(tempId,resobj);
            }
            resobj.m_FinishCallback(path,resobj.m_CloneObj,param1,param2,param3);
        }

    }

    /// <summary>
    ///回收资源
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="maxCacheCount"></param>
    /// <param name="destorycache"></param>
    /// <param name="recycleParent"></param>
    public void ReleaseObject(GameObject obj,int maxCacheCount=-1,bool destorycache=false,bool recycleParent=true)
    {
        if (obj == null) return;
        int guid = obj.GetInstanceID();

        ResourceObj resobj = null;
        if (!m_RescourceObjDic.TryGetValue(guid,out resobj))
        {
            Debug.LogError("对象不是ObjectManager 创建的" +obj.name);
            return;
        }

        if (resobj==null)
        {
            Debug.LogError("缓存的ResourceObj为空");
            return;
        }

        if (resobj.m_Already)
        {
            Debug.LogError("该对象已经放回对象池了 检查自己是否清空引用!");
            return;
        }

#if UNITY_EDITOR
        obj.name += "(Recycle)";
#endif
        List<ResourceObj> list = null;
        ///不缓存
        if (maxCacheCount==0)
        {
            m_RescourceObjDic.Remove(guid);
            ResourceManager.Instance.ReleaseResource(resobj,destorycache);
            resobj.Reset();
            m_ResourceObjPool.Recycle(resobj);
        }
        //回收到对象池
        else
        {
            if (!m_ObjectPoolDic.TryGetValue(resobj.m_Crc,out list)||list==null)
            {
                list = new List<ResourceObj>();
                m_ObjectPoolDic.Add(resobj.m_Crc,list);
            }
            if (resobj.m_CloneObj)
            {
                if (recycleParent)
                    resobj.m_CloneObj.transform.SetParent(this.m_RecycleTs);
                else
                    resobj.m_CloneObj.SetActive(false);
            }
            if (maxCacheCount < 0 ||  list.Count < maxCacheCount)
            {
                list.Add(resobj);
                resobj.m_Already = true;
                //resourceManager 做一个引用计数
                ResourceManager.Instance.DecreaseResourceRef(resobj);
            }
            //达到最大缓存个数
            else
            {
                m_RescourceObjDic.Remove(guid);
                ResourceManager.Instance.ReleaseResource(resobj, destorycache);
                resobj.Reset();
                m_ResourceObjPool.Recycle(resobj);
            }
        }
    }



    #region 类对象池使用

    protected Dictionary<Type, object> m_ClassPoolDic = new Dictionary<Type, object>();
    /// <summary>
    /// 创建类对象池 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="max"></param>
    /// <returns></returns>
    public ClassObjectPool<T> GetOrCreateClassPool<T>(int max) where T : class, new()
    {
        Type type = typeof(T);
        object outobj = null;
        if (!m_ClassPoolDic.TryGetValue(type, out outobj)|| outobj==null)
        {
            ClassObjectPool<T> pool = new ClassObjectPool<T>(max);
            m_ClassPoolDic.Add(type,pool);
            return pool;
        }
        return outobj as ClassObjectPool<T>;
    }


    /// <summary>
    /// 创建对象池且获取一个对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="maxcount"></param>
    /// <returns></returns>
    public T NewClassObjectFromPool<T>(int maxcount) where T : class, new()
    {
        ClassObjectPool<T> pool = new ClassObjectPool<T>(maxcount);
        if (pool == null) return null;
        return pool.Spwan(true);
    }

    #endregion
}
